/**
 * @file	
 * @author	chipsea
 * @brief	
 * @version	0.1
 * @date	2020-11-30
 * @copyright Copyright (c) 2020, CHIPSEA Co., Ltd.
 * @note
 */

/**************************************************************************************************
**************************************************************************************************/

#ifndef L2CAP_INTERNAL_H
#define L2CAP_INTERNAL_H

#ifdef __cplusplus
extern "C"
{
#endif

/*********************************************************************
 * INCLUDES
 */
#include "hci.h"
#include "l2cap.h"

/*********************************************************************
 * MACROS
 */

// Macro to see if a given channel is a fixed channel
#define FIX_CHANNEL( CID )        ( (CID) == L2CAP_CID_GENERIC ||\
                                    (CID) == L2CAP_CID_SIG ||\
                                    (CID) == L2CAP_CID_ATT ||\
                                    (CID) == L2CAP_CID_SMP )

// Marco to convert a channel ID to an index into L2CAP Channel table
#define CID_TO_INDEX( CID )       ( (CID) - BASE_DYNAMIC_CID )

// Marco to convert a fixed channel ID to an index into L2CAP Fixed Channel table
#define FCID_TO_INDEX( CID )      ( (CID) - L2CAP_CID_ATT )

// Macro to return the record maintained a given fix channel
#define FIX_CHANNEL_REC( CID )    ( l2capFixedChannels[FCID_TO_INDEX( CID )] )

/*********************************************************************
 * CONSTANTS
 */
// Signaling command header: Code (1 byte) + Identifier (1 byte) + Length (2 bytes)
#define SIGNAL_HDR_SIZE                            4

// Maximum size of data field of Signaling commands
#define SIGNAL_DATA_SIZE                           ( L2CAP_SIG_MTU_SIZE - SIGNAL_HDR_SIZE )

/*********************************************************************
 * L2CAP Channel States: states used for l2capChannel 'state' field
 */
// Closed - no channel associated with this CID
#define L2CAP_CLOSED                               0x00

// Waiting for Echo Response
#define L2CAP_W4_ECHO_RSP                          0x01

// Waiting for Info Response
#define L2CAP_W4_INFO_RSP                          0x02

// Waiting for Connection Parameter Update Response
#define L2CAP_W4_PARAM_UPDATE_RSP                  0x03

/*********************************************************************
 * TYPEDEFS
 */

// L2CAP Channel structure. Allocated one per application connection
// between two devices. CID assignment is relative to a particular device
// and a device can assign CIDs independently from other devices (except
// for the reserved CIDs). The CIDs are dynamically allocated in the range
// from 0x0040 to 0xFFFF.
typedef struct
{
  // Channel info
  uint8  state; // Channel connection state
  uint16 CID;   // Local channel id
  uint8  id;    // Local identifier - matches responses with requests

  // Link info
  uint16 connHandle; // link connection handle

  // Application info
  uint8 taskId; // task that channel belongs to

  // Timer id
  uint8 timerId;
} l2capChannel_t;

// L2CAP Fixed Channel structure. Allocated one for each fixed channel.
typedef struct
{
  uint16 CID;   // channel id
  uint8 taskId; // task registered with channel
} l2capFixedChannel_t;

// Signaling packet header format
typedef struct
{
  uint8 opcode; // type of command
  uint8 id;     // identifier - matches responses with requests
  uint16 len;   // length of data field (doesn't cover Code, Identifier and Length fields)
} l2capSignalHdr_t;

/**
 * @brief   Callback function prototype for building a Signaling command.
 *
 * @param   pBuf - pointer to buffer to hold command data
 * @param   pData - pointer to command data
 *
 * @return  length of the command data
 */
typedef uint16 (*pfnL2CAPBuildCmd_t)( uint8 *pBuf, uint8 *pData );

/*********************************************************************
 * GLOBAL VARIABLES
 */
extern uint8 l2capTaskID;
extern l2capChannel_t l2capChannels[];
extern l2capFixedChannel_t l2capFixedChannels[];

/*********************************************************************
 * FUNCTIONS - API
 */

/*
 *  Send L2CAP Command.
 */
extern bStatus_t l2capSendCmd( uint16 connHandle, uint8 opcode, uint8 id,
                               uint8 *pCmd, pfnL2CAPBuildCmd_t pfnBuildCmd );
/*
 * Send L2CAP Request.
 */
extern bStatus_t l2capSendReq( uint16 connHandle, uint8 opcode, uint8 *pReq,
                               pfnL2CAPBuildCmd_t pfnBuildCmd, uint8 state, uint8 taskId );
/*
 * Build Echo Request.
 */
extern uint16 l2capBuildEchoReq( uint8 *pBuf, uint8 *pCmd );

/*
 * Build Info Request.
 */
extern uint16 l2capBuildInfoReq( uint8 *pBuf, uint8 *pCmd );

/*
 * Build Parameter Update Request.
 */
extern uint16 l2capBuildParamUpdateReq( uint8 *pBuf, uint8 *pData );

/*
 * Encapsulate and send L2CAP packet.
 */
extern bStatus_t l2capEncapSendData( uint16 connHandle, l2capPacket_t *pPkt );

/*
 * Parse L2CAP packet.
 */
extern uint8 l2capParsePacket( l2capPacket_t *pPkt, hciDataEvent_t *pHciMsg );

/*
 * Parse L2CAP Signaling header.
 */
extern void l2capParseSignalHdr( l2capSignalHdr_t *pHdr, uint8 *pData );

/*
 * Build Echo Response.
 */
extern uint16 l2capBuildEchoRsp( uint8 *pBuf, uint8 *pCmd );

/*
 * Parse Command Reject.
 */
extern bStatus_t l2capParseCmdReject( l2capSignalCmd_t *pCmd, uint8 *pData, uint16 len );

/*
 * Parse Echo Response.
 */
extern bStatus_t l2capParseEchoRsp( l2capSignalCmd_t *pCmd, uint8 *pData, uint16 len );

/*
 * Parse Information Response.
 */
extern bStatus_t l2capParseInfoRsp( l2capSignalCmd_t *pCmd, uint8 *pData, uint16 len );

/*
 * Parse Connection Parameter Update Response.
 */
extern bStatus_t l2capParseParamUpdateRsp( l2capSignalCmd_t *pCmd, uint8 *pData, uint16 len );

/*
 * Find a channel using the local identifier.
 */
extern l2capChannel_t *l2capFindLocalId( uint8 id );

/*
 * Free a channel.
 */
extern void l2capFreeChannel( l2capChannel_t *pChannel );

/*
 * Stop an active timer for a given channel.
 */
extern void l2capStopTimer( l2capChannel_t *pChannel );

/*
 * Handle an incoming packet error.
 */
extern void l2capHandleRxError( uint16 connHandle );

/*
 * Forward a data message to upper layer application.
 */
extern bStatus_t l2capNotifyData( uint8 taskId, uint16 connHandle, l2capPacket_t *pPkt );

/*
 * Send a Signaling command to upper layer application.
 */
extern void l2capNotifySignal( uint8 taskId, uint16 connHandle, uint8 status,
                               uint8 opcode, uint8 id, l2capSignalCmd_t *pCmd );

extern void *L2CAP_Fragment_bm_alloc( uint16 size );

extern uint8 L2CAP_Fragment_SendDataPkt( uint16 connHandle, uint8 fragFlg,uint16 pktLen, uint8 *pBuf );


/*********************************************************************
 * @fn      l2capInfoRsp
 *
 * @brief   Send Info Response.
 *
 *          Use like: l2capInfoRsp( uint16 connHandle, uint8 id, l2capInfoRsp_t *pInfoRsp );
 *
 * @param   connHandle - connection to use
 * @param   id - identifier received in request
 * @param   pInfoRsp - pointer to Info Response to be sent
 *
 * @return  SUCCESS: Request was sent successfully.
 *          INVALIDPARAMETER: Data can not fit into one packet.
 *          MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
 *          bleNotConnected: Connection is down.
 *          bleMemAllocError: Memory allocation error occurred.
 */
#define l2capInfoRsp( connHandle, id, pInfoRsp )  l2capSendCmd( (connHandle), L2CAP_INFO_RSP, (id),\
                                                                (uint8 *)(pInfoRsp), L2CAP_BuildInfoRsp )

/*********************************************************************
 * @fn      l2capEchoRsp
 *
 * @brief   Send Ehco Response.
 *
 *          Use like: l2capEchoRsp( uint16 connHandle, uint8 id, l2capEchoRsp_t *pEchoRsp );
 *
 * @param   connHandle - connection to use
 * @param   id - identifier received in request
 * @param   pEchoRsp - pinter to Echo Response to be sent
 *
 * @return  SUCCESS: Request was sent successfully.
 *          INVALIDPARAMETER: Data can not fit into one packet.
 *          MSG_BUFFER_NOT_AVAIL: No HCI buffer is available.
 *          bleNotConnected: Connection is down.
 *          bleMemAllocError: Memory allocation error occurred.
 */
#define l2capEchoRsp( connHandle, id, pEchoRsp )  l2capSendCmd( (connHandle), L2CAP_ECHO_RSP, (id),\
                                                                (uint8 *)(pEchoRsp), l2capBuildEchoRsp )


/*********************************************************************
*********************************************************************/

#ifdef __cplusplus
}
#endif

#endif /* L2CAP_INTERNAL_H */
